//go:build windows
// +build windows

// Package dll类 备注, 由go的syscall包复制过来修改的.
package win系统类

import (
	"golang.org/x/sys/windows"
	"syscall"
	"unsafe"
)

/*
winapi文档
https://learn.microsoft.com/zh-cn/windows/win32/api/winuser/nf-winuser-getclassnamew
*/
type lazyDLL struct {
	dll对象 windows.LazyDLL
}
type lazyProc struct {
	addr uintptr
}

// DLL载入 尝试加载指定的DLL文件，并创建一个与之关联的gmap对象。
// 参数:
// dll名称 - 需要载入的DLL文件的名称或者路径,如果"dll名称"不是绝对路径，也不是Go使用的已知系统DLL，Windows将在许多位置搜索命名的DLL
// 返回值:
// *lazyDLL - 如果成功载入DLL并创建gmap对象，则返回dll结构体指针。
// error - 如果载入DLL或创建gmap对象失败，则返回错误信息。
func DLL载入(dll名称 string) *lazyDLL {
	/*
		2024-09-11 备注
		不能使用syscall.NewLazyDLL二次包装, 会导致有时候调用不到dll, 具体原因不详
		所以, 改成同windows包一样的调用方式.
	*/
	dllObj := windows.NewLazySystemDLL(dll名称)
	return &lazyDLL{*dllObj}
}
func (d *lazyDLL) X命令(命令名 string) *lazyProc {
	命令地址 := d.dll对象.NewProc(命令名).Addr()
	if 命令地址 == 0 {
		panic("调用命令错误, 命令未找到:" + 命令名)
	}
	return &lazyProc{命令地址}
}

// X调用 如果函数需要参数，你需要将它们转换为 uintptr 类型。对于字符串参数，通常需要将它们转换为 UTF-16 编码的指针。
// 对于整型参数，可以使用类似 uintptr(value) 的方式转换。
// 如果函数需要一个指向内存的指针，你可以创建一个 []byte 并获取它的地址。
// 返回三个值：第一个是 uintptr 类型的结果，第二个是 uintptr 类型的错误代码，第三个是 error 类型的错误对象。根据函数的不同，可能需要对这些返回值进行适当的转换。
//
// 类型备注:
//基本整数类型
//BYTE: 对应 uint8
//WORD: 对应 uint16
//DWORD: 对应 uint32
//LONG: 对应 int32
//INT: 对应 int
//UINT: 对应 uint
//ULONG: 对应 uint32
//LONG_PTR / LPARAM: 通常对应 uintptr (在 32 位系统上为 int32，在 64 位系统上为 int64)
//WPARAM: 通常对应 uintptr
//
//浮点数类型
//FLOAT: 对应 float32
//DOUBLE: 对应 float64
//
//字符串类型
//LPCSTR: 对应 *byte 或 []byte
//LPCWSTR: 对应 *uint16 或 []uint16
//LPCTSTR: 根据字符集，可以是 LPCSTR 或 LPCWSTR
//
//结构体类型
//POINT: 对应 struct { X, Y int32 }
//RECT: 对应 struct { Left, Top, Right, Bottom int32 }
//HWND: 对应 uintptr (窗口句柄)
//HINSTANCE: 对应 uintptr (模块句柄)
//HICON: 对应 uintptr (图标句柄)
//HBRUSH: 对应 uintptr (画刷句柄)
//HDC: 对应 uintptr (设备上下文句柄)
//
//布尔类型
//BOOL: 对应 int32 或 bool (通常 int32，因为 Windows API 中 BOOL 实际上是 int 的别名)
//
//指针类型
//LPVOID / PVOID: 对应 unsafe.Pointer
//LPCVOID: 对应 unsafe.Pointer
//
//特殊类型
//ATOM: 对应 uint16
//NTSTATUS: 对应 int32

func (d *lazyProc) X调用(参数 ...uintptr) (r1, r2 uintptr, lastErr error) {
	return syscall.SyscallN(d.addr, 参数...)
}

/*
技巧备注:

当你尝试将 -1（一个负数）直接转换为 uintptr（一个无符号整数类型）时，编译器会报错，因为它无法确定如何安全地处理这个负数
可以把负数先定义成一个变量后再转换, 就不会报错. 并且只能定义变量, 不能定义成常量.
var signedInt int = -1
user32.X调用("SetWindowPos", uintptr(窗口句柄), uintptr(signedInt))//这样就不会报错
*/

// DLL_go文本到UTF16文本_LPCWSTR 将Go字符串转换为Windows字符指针，用于DLL调用。
// 参数s: 需要转换的Go字符串。
// 返回值: 返回一个uintptr，指向转换后的Windows字符。
// 备注:：LPCWSTR是一个指向宽字符（UTF-16）字符串的常量指针。适用于Windows API函数：通常用于调用需要宽字符字符串指针的API函数，例如 SetWindowTextW。
// LPCWSTR类型参考例子 https://learn.microsoft.com/zh-cn/windows/win32/api/winuser/nf-winuser-setwindowtextw
func DLL_go文本到UTF16文本_LPCWSTR(文本 string) uintptr {
	// 当输入字符串为空时，直接返回0，避免不必要的转换操作。
	if 文本 == "" {
		return 0
	}
	// 使用windows包中的StringToUTF16Ptr函数将Go字符串转换为UTF-16编码的指针。
	ptr := windows.StringToUTF16Ptr(文本)
	// 将转换后的字符串指针转换为uintptr，以便在DLL调用时使用。
	return uintptr(unsafe.Pointer(ptr))
}

// DLL_go文本到缓冲区_LPARAM 将Go字符串转换为Windows API所需的LPARAM类型
// 这个函数主要用于与DLL（动态链接库）交互时，将Go字符串转换为LPARAM类型
// 参数s是需要转换的Go字符串
// 返回值是转换后的LPARAM类型值，如果输入字符串为空，则返回0
// LPARAM类型参考例子 https://learn.microsoft.com/zh-cn/windows/win32/api/winuser/nf-winuser-sendmessagew
func DLL_go文本到缓冲区_LPARAM(文本 string) uintptr {
	// 当输入字符串为空时，直接返回0，LPARAM类型通常用于Windows API调用，null值有特殊含义
	if 文本 == "" {
		return 0
	}
	// 创建一个足够大的缓冲区来存放字符串，额外加1个字节用于存储null终止符
	buffer := make([]byte, len(文本)+1)
	// 将字符串s复制到缓冲区buffer中
	copy(buffer, []byte(文本))
	// 在字符串末尾添加null终止符，这是C语言字符串的标准
	buffer[len(文本)] = 0

	// 将缓冲区的地址转换为uintptr类型，然后将其视为LPARAM类型返回
	// 这样DLL就可以正确地访问这个字符串
	return uintptr(unsafe.Pointer(&buffer[0]))
}

// DLL_go整数到指针_LPDWORD 将Go中的int32指针转换为LPDWORD（长指针）
// 此函数主要用于与C语言编写的DLL进行交互，因为C语言中使用LPDWORD来传递整数指针
// 备注:LPDWORD类型是整数变量的指针, 用于传递整数变量指针给C
// 参数:
// value: 指向Go中int32变量的指针，用于与DLL进行数据交换
// 返回值:
// uintptr: 返回转换后的LPDWORD值，以便在DLL调用时使用
func DLL_go整数到指针_LPDWORD(值 *int32) uintptr {
	return uintptr(unsafe.Pointer(值))
	//uintptr(unsafe.Pointer(pid))
}

func DLL_go布尔值转换_BOOL(b bool) uintptr {
	if b {
		return 1
	} else {
		return 0
	}
}
